﻿using System;
using System.Diagnostics;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using E = com.bodurov.SilverlightControls.XmlCodeEditor.Enums.XmlElement;
using A = com.bodurov.SilverlightControls.XmlCodeEditor.Enums.XmlArea;

namespace com.bodurov.SilverlightControls.XmlCodeEditor.Tests
{
    [TestClass]//[Ignore]
    public class IterativeTokenizerCanParseComplexTags : BaseTokenFixture
    {

        [TestMethod]//[Ignore]
        public void CanAddTagWithDualQuoteAttribute()
        {
            const string str = "<hr color=\"red\" />";

            var tokenizer = new IterativeTokenizer();
            var tokens = tokenizer.ParseTokens(str);

            //TraceTokens(tokens);

            AssertTokens(tokens,
                Within(A.InsideTag,
                    Expect("<", E.OpeningTagBeginBracket),
                    Expect("hr", E.OpeningTagName),
                    Expect(" ", E.WhiteSpace),
                    Expect("color", E.AttributeName),
                    Expect("=", E.AttributeEqualSign),
                    Expect("\"red\"", E.AttributeDualQuoteString),
                    Expect(" ", E.WhiteSpace),
                    Expect("/>", E.OpeningTagClosingBracket)));
        }

        [TestMethod]//[Ignore]
        public void CanAddTagWithDualQuoteAttribute_WithEscapedQuote()
        {
            const string str = @"<img title=""image #\""1\"""" />";

            var tokenizer = new IterativeTokenizer();
            var tokens = tokenizer.ParseTokens(str);

            //TraceTokens(tokens);

            AssertTokens(tokens,
                Within(A.InsideTag,
                    Expect("<", E.OpeningTagBeginBracket),
                    Expect("img", E.OpeningTagName),
                    Expect(" ", E.WhiteSpace),
                    Expect("title", E.AttributeName),
                    Expect("=", E.AttributeEqualSign),
                    Expect(@"""image #\""1\""""", E.AttributeDualQuoteString),
                    Expect(" ", E.WhiteSpace),
                    Expect("/>", E.OpeningTagClosingBracket)));
        }

        [TestMethod]//[Ignore]
        public void CanAddTagWithMultipleAttributes()
        {
            const string str = "<img src='/folder1/image_1.gif' border=1 \n"+
                                    "\t title=\"image #\\\"1\\\"\"/>";

            var tokenizer = new IterativeTokenizer();
            var tokens = tokenizer.ParseTokens(str);

            //TraceTokens(tokens);

            AssertTokens(tokens,
                Within(A.InsideTag,
                    Expect("<", E.OpeningTagBeginBracket),
                    Expect("img", E.OpeningTagName),
                    Expect(" ", E.WhiteSpace),
                    Expect("src", E.AttributeName),
                    Expect("=", E.AttributeEqualSign),
                    Expect(@"'/folder1/image_1.gif'", E.AttributeSingleQuoteString),
                    Expect(" ", E.WhiteSpace),
                    Expect("border", E.AttributeName),
                    Expect("=", E.AttributeEqualSign),
                    Expect(@"1", E.AttributeLiteralContent),
                    Expect(" ", E.WhiteSpace),
                    Expect_NewLine,
                    Expect("\t ", E.WhiteSpace),
                    Expect("title", E.AttributeName),
                    Expect("=", E.AttributeEqualSign),
                    Expect(@"""image #\""1\""""", E.AttributeDualQuoteString),
                    Expect("/>", E.OpeningTagClosingBracket)));
        }

        [TestMethod]//[Ignore]
        public void CanAddTagWithMultipleAttributesAndInnerContent()
        {
            const string str = "<a href='/Home.html' style=\"border:solid 1px red\" \n"+
                                "\t title=\"link #\\\"1\\\"\">Home</a>";

            var tokenizer = new IterativeTokenizer();
            var tokens = tokenizer.ParseTokens(str);

            //TraceTokens(tokens);

            AssertTokens(tokens,
                Within(A.InsideTag,
                    Expect("<", E.OpeningTagBeginBracket),
                    Expect("a", E.OpeningTagName),
                    Expect(" ", E.WhiteSpace),
                    Expect("href", E.AttributeName),
                    Expect("=", E.AttributeEqualSign),
                    Expect("'/Home.html'", E.AttributeSingleQuoteString),
                    Expect(" ", E.WhiteSpace),
                    Expect("style", E.AttributeName),
                    Expect("=", E.AttributeEqualSign),
                    Expect("\"border:solid 1px red\"", E.AttributeDualQuoteString),
                    Expect(" ", E.WhiteSpace),
                    Expect_NewLine,
                    Expect("\t ", E.WhiteSpace),
                    Expect("title", E.AttributeName),
                    Expect("=", E.AttributeEqualSign),
                    Expect("\"link #\\\"1\\\"\"", E.AttributeDualQuoteString),
                    Expect(">", E.OpeningTagEndBracket)),
              Within(A.OutsideTag, 
                    Expect("Home", E.OutsideTagContent)),
              Within(A.InsideTag,
                    Expect("</", E.ClosingTagBeginBracket),
                    Expect("a", E.ClosingTagName),
                    Expect(">", E.ClosingTagEndBracket)));
        }

        [TestMethod]//[Ignore]
        public void CanParseComplexTags_Case_1()
        {
            const string str = "Before Anything<table><tr>\n"+
                                    "\t<td align = right >Cell 1</td>\n"+
                                    "\t<td>&nbsp;</td>\n"+
                                    "</tr></table>After All";

            var tokenizer = new IterativeTokenizer();
            var tokens = tokenizer.ParseTokens(str);


            //TraceTokens(tokens);

            AssertTokens(tokens,
                Within(A.OutsideTag,
                    Expect("Before Anything", E.OutsideTagContent)),
                Within(A.InsideTag,
                    Expect("<", E.OpeningTagBeginBracket),
                    Expect("table", E.OpeningTagName),
                    Expect(">", E.OpeningTagEndBracket),
                    Expect("<", E.OpeningTagBeginBracket),
                    Expect("tr", E.OpeningTagName),
                    Expect(">", E.OpeningTagEndBracket),
                    Expect_NewLine),
                Within(A.OutsideTag,
                    Expect("\t", E.OutsideTagContent)),
                Within(A.InsideTag,
                    Expect("<", E.OpeningTagBeginBracket),
                    Expect("td", E.OpeningTagName),
                    Expect(" ", E.WhiteSpace),
                    Expect("align", E.AttributeName),
                    Expect(" ", E.WhiteSpace),
                    Expect("=", E.AttributeEqualSign),
                    Expect(" ", E.WhiteSpace),
                    Expect(@"right", E.AttributeLiteralContent),
                    Expect(" ", E.WhiteSpace),
                    Expect(">", E.OpeningTagEndBracket)),
              Within(A.OutsideTag,
                    Expect("Cell 1", E.OutsideTagContent)),
              Within(A.InsideTag, 
                    Expect("</", E.ClosingTagBeginBracket),
                    Expect("td", E.ClosingTagName),
                    Expect(">", E.ClosingTagEndBracket),
                    Expect_NewLine),
              Within(A.OutsideTag,
                    Expect("\t", E.OutsideTagContent)),
              Within(A.InsideTag, 
                    Expect("<", E.OpeningTagBeginBracket),
                    Expect("td", E.OpeningTagName),
                    Expect(">", E.OpeningTagEndBracket)),
              Within(A.OutsideTag,
                    Expect("&nbsp;", E.Entity)),
              Within(A.InsideTag,
                    Expect("</", E.ClosingTagBeginBracket),
                    Expect("td", E.ClosingTagName),
                    Expect(">", E.ClosingTagEndBracket),
                    Expect_NewLine),
              Within(A.InsideTag, 
                    Expect("</", E.ClosingTagBeginBracket),
                    Expect("tr", E.ClosingTagName),
                    Expect(">", E.ClosingTagEndBracket),
                    Expect("</", E.ClosingTagBeginBracket),
                    Expect("table", E.ClosingTagName),
                    Expect(">", E.ClosingTagEndBracket)),
              Within(A.OutsideTag,
                    Expect("After All", E.OutsideTagContent)));
        }


        [TestMethod]//[Ignore]
        public void CanParseComplexTags_Case_2()
        {
            const string str = "&<table><tr>\n" +
                                    "\t<td align='right'>&</td>\n" +
                                    "\t<td>&nbsp;<br />&nbsp;</td>\n" +
                                    "</tr></table>B";

            var tokenizer = new IterativeTokenizer();
            var tokens = tokenizer.ParseTokens(str);

            //TraceTokens(tokens);

            AssertTokens(tokens,
                Within(A.OutsideTag,
                    Expect("&", E.OutsideTagContent)),
                Within(A.InsideTag,
                    Expect("<", E.OpeningTagBeginBracket),
                    Expect("table", E.OpeningTagName),
                    Expect(">", E.OpeningTagEndBracket),
                    Expect("<", E.OpeningTagBeginBracket),
                    Expect("tr", E.OpeningTagName),
                    Expect(">", E.OpeningTagEndBracket),
                    Expect_NewLine),
                Within(A.OutsideTag,
                    Expect("\t", E.OutsideTagContent)),
                Within(A.InsideTag,
                    Expect("<", E.OpeningTagBeginBracket),
                    Expect("td", E.OpeningTagName),
                    Expect(" ", E.WhiteSpace),
                    Expect("align", E.AttributeName),
                    Expect("=", E.AttributeEqualSign),
                    Expect(@"'right'", E.AttributeSingleQuoteString),
                    Expect(">", E.OpeningTagEndBracket)),
              Within(A.OutsideTag,
                    Expect("&", E.OutsideTagContent)),
              Within(A.InsideTag,
                    Expect("</", E.ClosingTagBeginBracket),
                    Expect("td", E.ClosingTagName),
                    Expect(">", E.ClosingTagEndBracket),
                    Expect_NewLine),
              Within(A.OutsideTag,
                    Expect("\t", E.OutsideTagContent)),
              Within(A.InsideTag,
                    Expect("<", E.OpeningTagBeginBracket),
                    Expect("td", E.OpeningTagName),
                    Expect(">", E.OpeningTagEndBracket)),
              Within(A.OutsideTag,
                    Expect("&nbsp;", E.Entity)),
               Within(A.InsideTag,
                    Expect("<", E.OpeningTagBeginBracket),
                    Expect("br", E.OpeningTagName),
                    Expect(" ", E.WhiteSpace),
                    Expect("/>", E.OpeningTagClosingBracket)),
              Within(A.OutsideTag,
                    Expect("&nbsp;", E.Entity)),
              Within(A.InsideTag,
                    Expect("</", E.ClosingTagBeginBracket),
                    Expect("td", E.ClosingTagName),
                    Expect(">", E.ClosingTagEndBracket),
                    Expect_NewLine),
              Within(A.InsideTag,
                    Expect("</", E.ClosingTagBeginBracket),
                    Expect("tr", E.ClosingTagName),
                    Expect(">", E.ClosingTagEndBracket),
                    Expect("</", E.ClosingTagBeginBracket),
                    Expect("table", E.ClosingTagName),
                    Expect(">", E.ClosingTagEndBracket)),
              Within(A.OutsideTag,
                    Expect("B", E.OutsideTagContent)));
        }

        [TestMethod]//[Ignore]
        public void CanParseComplexTags_Case_3()
        {
            var str = Lines(
                    "<!DOCTYPE html> ",
                    "<html lang=\"en\" dir=\"ltr\"> ",
                    "<!-- machid: \"somecomment\" &nbsp; <br/> --> ",
                    "<head>",
                    "		<script>",
                    "			var i = 1;",
                    "			var j = 2;",
                    "			if(i < j) alert('yes');",
                    "		</script>",
                    "		<title>title</title> ",
                    "    <link rel=\"search\" type='application/opensearchdescription+xml'",
                    " \t   href= \"http://www.abc.com\" ",
                    " \t   title=\"ABC\"> ",
                    "</head> ",
                    "<body class=\"watch5-active date-20100425 en_US is-english\"> ",
                    "	<!--[if IE]><div id=\"ie\"><![endif]--> ",
                    "</body>",
                    "</html>");

            var tokenizer = new IterativeTokenizer();
            //var watch = Stopwatch.StartNew();
            var tokens = tokenizer.ParseTokens(str);
            //watch.Stop();

            //Dbg.Trace(">>>>" + watch.ElapsedMilliseconds);

            TraceTokens(tokens);

            //BuildAssertions(tokens);

            



            AssertTokens(tokens,
                Within(A.InsideTag,
                    Expect("<", E.OpeningTagBeginBracket),
                    Expect("!DOCTYPE", E.OpeningTagName),
                    Expect(" ", E.WhiteSpace),
                    Expect("html", E.AttributeName),
                    Expect(">", E.OpeningTagEndBracket)),
                Within(A.OutsideTag,
                    Expect(" ", E.OutsideTagContent),
                    Expect_NewLine),
                Within(A.InsideTag,
                    Expect("<", E.OpeningTagBeginBracket),
                    Expect("html", E.OpeningTagName),
                    Expect(" ", E.WhiteSpace),
                    Expect("lang", E.AttributeName),
                    Expect("=", E.AttributeEqualSign),
                    Expect("\"en\"", E.AttributeDualQuoteString),
                    Expect(" ", E.WhiteSpace),
                    Expect("dir", E.AttributeName),
                    Expect("=", E.AttributeEqualSign),
                    Expect("\"ltr\"", E.AttributeDualQuoteString),
                    Expect(">", E.OpeningTagEndBracket)),
                Within(A.OutsideTag,
                    Expect(" ", E.OutsideTagContent),
                    Expect_NewLine),
                Within(A.InsideClientSideComment,
                    Expect("<!--", E.OpeningClientSideCommentBracket),
                    Expect(" machid: \"somecomment\" &nbsp; <br/> ", E.InsideClientSideComment),
                    Expect("-->", E.ClosingClientSideCommentBracket)),
                Within(A.OutsideTag,
                    Expect(" ", E.OutsideTagContent),
                    Expect_NewLine),
                Within(A.InsideTag,
                    Expect("<", E.OpeningTagBeginBracket),
                    Expect("head", E.OpeningTagName),
                    Expect(">", E.OpeningTagEndBracket),
                    Expect_NewLine),
                Within(A.OutsideTag,
                    Expect("		", E.OutsideTagContent)),
                Within(A.InsideTag,
                    Expect("<", E.OpeningTagBeginBracket),
                    Expect("script", E.OpeningTagName),
                    Expect(">", E.OpeningTagEndBracket),
                    Expect_NewLine),
                Within(A.OutsideTag,
                    Expect("			var i = 1;", E.OutsideTagContent),
                    Expect_NewLine,
                    Expect("			var j = 2;", E.OutsideTagContent),
                    Expect_NewLine,
                    Expect("			if(i < j) alert('yes');", E.OutsideTagContent),
                    Expect_NewLine,
                    Expect("		", E.OutsideTagContent)),
                Within(A.InsideTag,
                    Expect("</", E.ClosingTagBeginBracket),
                    Expect("script", E.ClosingTagName),
                    Expect(">", E.ClosingTagEndBracket),
                    Expect_NewLine),
                Within(A.OutsideTag,
                    Expect("		", E.OutsideTagContent)),
                Within(A.InsideTag,
                    Expect("<", E.OpeningTagBeginBracket),
                    Expect("title", E.OpeningTagName),
                    Expect(">", E.OpeningTagEndBracket)),
                Within(A.OutsideTag,
                    Expect("title", E.OutsideTagContent)),
                Within(A.InsideTag,
                    Expect("</", E.ClosingTagBeginBracket),
                    Expect("title", E.ClosingTagName),
                    Expect(">", E.ClosingTagEndBracket)),
                Within(A.OutsideTag,
                    Expect(" ", E.OutsideTagContent),
                    Expect_NewLine,
                    Expect("    ", E.OutsideTagContent)),
                Within(A.InsideTag,
                    Expect("<", E.OpeningTagBeginBracket),
                    Expect("link", E.OpeningTagName),
                    Expect(" ", E.WhiteSpace),
                    Expect("rel", E.AttributeName),
                    Expect("=", E.AttributeEqualSign),
                    Expect("\"search\"", E.AttributeDualQuoteString),
                    Expect(" ", E.WhiteSpace),
                    Expect("type", E.AttributeName),
                    Expect("=", E.AttributeEqualSign),
                    Expect("'application/opensearchdescription+xml'", E.AttributeSingleQuoteString),
                    Expect_NewLine,
                    Expect(" \t   ", E.WhiteSpace),
                    Expect("href", E.AttributeName),
                    Expect("=", E.AttributeEqualSign),
                    Expect(" ", E.WhiteSpace),
                    Expect("\"http://www.abc.com\"", E.AttributeDualQuoteString),
                    Expect(" ", E.WhiteSpace),
                    Expect_NewLine,
                    Expect(" \t   ", E.WhiteSpace),
                    Expect("title", E.AttributeName),
                    Expect("=", E.AttributeEqualSign),
                    Expect("\"ABC\"", E.AttributeDualQuoteString),
                    Expect(">", E.OpeningTagEndBracket)),
                Within(A.OutsideTag,
                    Expect(" ", E.OutsideTagContent),
                    Expect_NewLine),
                Within(A.InsideTag,
                    Expect("</", E.ClosingTagBeginBracket),
                    Expect("head", E.ClosingTagName),
                    Expect(">", E.ClosingTagEndBracket)),
                Within(A.OutsideTag,
                    Expect(" ", E.OutsideTagContent),
                    Expect_NewLine),
                Within(A.InsideTag,
                    Expect("<", E.OpeningTagBeginBracket),
                    Expect("body", E.OpeningTagName),
                    Expect(" ", E.WhiteSpace),
                    Expect("class", E.AttributeName),
                    Expect("=", E.AttributeEqualSign),
                    Expect("\"watch5-active date-20100425 en_US is-english\"", E.AttributeDualQuoteString),
                    Expect(">", E.OpeningTagEndBracket)),
                Within(A.OutsideTag,
                    Expect(" ", E.OutsideTagContent),
                    Expect_NewLine,
                    Expect("	", E.OutsideTagContent)),
                Within(A.InsideClientSideComment,
                    Expect("<!--", E.OpeningClientSideCommentBracket),
                    Expect("[if IE]><div id=\"ie\"><![endif]", E.InsideClientSideComment),
                    Expect("-->", E.ClosingClientSideCommentBracket)),
                Within(A.OutsideTag,
                    Expect(" ", E.OutsideTagContent),
                    Expect_NewLine),
                Within(A.InsideTag,
                    Expect("</", E.ClosingTagBeginBracket),
                    Expect("body", E.ClosingTagName),
                    Expect(">", E.ClosingTagEndBracket),
                    Expect_NewLine),
                Within(A.InsideTag,
                    Expect("</", E.ClosingTagBeginBracket),
                    Expect("html", E.ClosingTagName),
                    Expect(">", E.ClosingTagEndBracket)));
        }


        [TestMethod]//[Ignore]
        public void CanParseDoctype()
        {
            const string str = "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML \n"+
                                "\t1.1//EN\" \"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd\">";

            var tokenizer = new IterativeTokenizer();
            var tokens = tokenizer.ParseTokens(str);

            //TraceTokens(tokens);

            AssertTokens(tokens,
                 Within(A.InsideTag,
                        Expect("<", E.OpeningTagBeginBracket),
                        Expect("!DOCTYPE", E.OpeningTagName),
                        Expect(" ", E.WhiteSpace),
                        Expect("html", E.AttributeName),
                        Expect(" ", E.WhiteSpace),
                        Expect("PUBLIC", E.AttributeName),
                        Expect(" ", E.WhiteSpace),
                        Expect("\"-//W3C//DTD XHTML ", E.AttributeDualQuoteString),
                        Expect_NewLine,
                        Expect("\t1.1//EN\"", E.AttributeDualQuoteString),
                        Expect(" ", E.WhiteSpace),
                        Expect("\"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd\"", E.AttributeDualQuoteString),
                        Expect(">", E.OpeningTagEndBracket)));
        }

        [TestMethod]//[Ignore]
        public void CanParseServerDeclaration()
        {
            const string str = "<% @ Master Language=\"VB\" %>";

            var tokenizer = new IterativeTokenizer();
            var tokens = tokenizer.ParseTokens(str);

            //TraceTokens(tokens);

            AssertTokens(tokens,
                Within(A.InsideServerDeclaration,
                    Expect("<%", E.OpeningServerScriptBracket),
                    Expect(" ", E.WhiteSpace),
                    Expect("@", E.ServerBlockSpecialChar),
                    Expect(" ", E.WhiteSpace),
                    Expect("Master", E.OpeningTagName),
                    Expect(" ", E.WhiteSpace),
                    Expect("Language", E.AttributeName),
                    Expect("=", E.AttributeEqualSign),
                    Expect("\"VB\"", E.AttributeDualQuoteString),
                    Expect(" ", E.WhiteSpace),
                    Expect("%>", E.ClosingServerScriptBracket)));
        }

        [TestMethod]//[Ignore]
        public void CanParseClientSideComment()
        {
            const string str = "<% @ Master Language=\"VB\" %>\n"+
                               "\t<div><!--\n"+
                               "\t<span=\"something \\\"1\\\"inside\"/>\n"+
                               "\t--></div>";

            var tokenizer = new IterativeTokenizer();
            var tokens = tokenizer.ParseTokens(str);

            //TraceTokens(tokens);
            
            AssertTokens(tokens,
                Within(A.InsideServerDeclaration,
                    Expect("<%", E.OpeningServerScriptBracket),
                    Expect(" ", E.WhiteSpace),
                    Expect("@", E.ServerBlockSpecialChar),
                    Expect(" ", E.WhiteSpace),
                    Expect("Master", E.OpeningTagName),
                    Expect(" ", E.WhiteSpace),
                    Expect("Language", E.AttributeName),
                    Expect("=", E.AttributeEqualSign),
                    Expect("\"VB\"", E.AttributeDualQuoteString),
                    Expect(" ", E.WhiteSpace),
                    Expect("%>", E.ClosingServerScriptBracket),
                    Expect_NewLine),
                Within(A.OutsideTag,
                    Expect("\t", E.OutsideTagContent)),
                Within(A.InsideTag,
                    Expect("<", E.OpeningTagBeginBracket),
                    Expect("div", E.OpeningTagName),
                    Expect(">", E.OpeningTagEndBracket)),
                Within(A.InsideClientSideComment,
                    Expect("<!--", E.OpeningClientSideCommentBracket),
                    Expect_NewLine,
                    Expect("\t<span=\"something \\\"1\\\"inside\"/>", E.InsideClientSideComment),
                    Expect_NewLine,
                    Expect("\t", E.InsideClientSideComment),
                    Expect("-->", E.ClosingClientSideCommentBracket)),
                Within(A.InsideTag,
                    Expect("</", E.ClosingTagBeginBracket),
                    Expect("div", E.ClosingTagName),
                    Expect(">", E.ClosingTagEndBracket)));
        }

        [TestMethod]//[Ignore]
        public void CanParsePartialTag()
        {
            const string str = "<asp:contentplaceholder";

            var tokenizer = new IterativeTokenizer();
            var tokens = tokenizer.ParseTokens(str);

            //TraceTokens(tokens);

            AssertTokens(tokens,
                 Within(A.InsideTag,
                    Expect("<", E.OpeningTagBeginBracket),
                    Expect("asp:", E.OpeningTagNamespace),
                    Expect("contentplaceholder", E.OpeningTagName)));
        }
    }
}
